home *** CD-ROM | disk | FTP | other *** search
/ Network Support Library / RoseWare - Network Support Library.iso / windows / bsdup2.exe / VIPX.DOC < prev   
Text File  |  1993-10-19  |  33KB  |  942 lines

  1. VIPX.386 Description, Limitations and Configuration Document
  2.  
  3. Current Version:        1.17
  4. Date:                   930819
  5.  
  6. Table of Contents
  7. -----------------
  8. I.   Description
  9. II.  Limitations
  10.     A.  Windows 3.X Versions
  11.     B.  Version Compatibility with Dedicated IPX
  12.     C.  Packet Size Limitations
  13.     D.  Memory Manager Limitations
  14. III. System Settings in the SYSTEM.INI File
  15.     A.  [386Enh] Section
  16.         1.  TimerCriticalSection
  17.     B.  [VIPX] Section
  18.         1.  VipxMappingPages
  19.         2.  VipxFailOverSizedPackets
  20.         3.  VpicdFix
  21.         4.  AutoIrqVirtualize
  22.         5.  VirtualizeIrq[0-F]
  23.         6.  VipxErrorMessages
  24.         7.  VipxWarningMessages
  25.         8.  VipxOutDebugStrOnErrors
  26.         9.  VipxOutDebugStrOnWarnings
  27.         10. VipxBreakOnErrors
  28.         11. VipxBreakOnWarnings
  29. IV.  Programming to the VIPX Interface
  30.     A.  Getting the VIPX API Entry Point
  31.     B.  Memory Management Issues
  32.     C.  APIs Specific to VIPX.386
  33.     D.  Handling Reentrancy Issues
  34.     E.  Using NWIPXSPX.DLL
  35. V.   Global DOS TSRs using IPX/SPX
  36.  
  37.  
  38. I.  Description
  39. ---------------
  40.  
  41. VIPX.386 is a Windows 3.X virutalization driver for IPXODI.COM
  42. driver.  It virtualizes requests to the globally loaded IPX driver. 
  43. When a request is made to IPX, VIPX will allocate a request buffer in
  44. the system's global memory, copy the original request to the global
  45. buffer and give the global request to IPX.  When the global request
  46. completes, IPX will call VIPX.  VIPX will then copy any results back
  47. to the original request buffer and call the application which
  48. submitted the request.
  49.  
  50.  
  51. II.  Limitations
  52. ----------------
  53.  
  54.  
  55. II.A.  Windows Versions
  56. -----------------------
  57.  
  58. VIPX supports Windows versions 3.0, 3.0a and 3.1.  However, there is
  59. some functionality which VIPX will only support in Windows 3.1. This
  60. is LAN IRQ virtualization for deadlock avoidence.  Under WIndows 3.0
  61. and 3.0a, VIPX will not virtualize LAN IRQs.  See section III.B.5
  62. VirtualizeIrq[0-F].
  63.  
  64. The preferred version of Windows is a patch of Windows 3.1. 
  65. Microsoft found a bug in the their Virtual Timer Device (VTD) driver
  66. which would cause a deadlock with the NetWare shell (NETX).  This
  67. version of WIN386.EXE has a single byte patch which prevents the
  68. situation from occurring. 
  69.  
  70. The fix is in the Vitual Timer Device's control procedure. Without
  71. the patch, a certain procedure is called during the Create_VM state. 
  72. With the patch, this procedure is now called during the
  73. VM_Critical_Init state.  The patch is applied at offset 441D8 in
  74. WIN386.EXE (544789 03-01-92 3:10a).  The value at that location is 07
  75. (Create_VM).  The value should be changed to 08 (VM_Critical_Init).
  76.  
  77. You may also address this bug by contacting Microsoft.  They have created
  78. a VTDA.386 driver.  To obtain the driver from Microsoft, call their BBS
  79. at 206-936-6735.  The name of the file to download is WW0863.EXE.
  80.  
  81. The deadlock occurs as a result of the shell waiting for the timer
  82. tick to advance.  When an NCP is sent, the shell will start it's
  83. retry timeout countdown.  This is essentially a loop waiting for the
  84. timer tick value to advance to a certain point.  The deadlock occurs
  85. when the tick value never increments and thus the shell is left in an
  86. infinite loop.  The reason the tick count is never incremented is
  87. that the timer interrupt is not allowed to be simulated into V86 mode
  88. under certain circumstances.  The patch to the VTD allows the timer
  89. interrupts to be simulated.
  90.  
  91.  
  92.  
  93. II.B.  Version Compatibility with Dedicated IPX
  94. -----------------------------------------------
  95.  
  96. Novell offically ceased maintenence on the dedicate IPX driver
  97. (IPX.OBJ) verion 3.10 dated 11-21-91.  The last version of VIPX to
  98. explicitly support that driver is 1.10.  The current version of VIPX
  99. supports IPXODI.COM only.  It is strongly recommended that you update
  100. your dedicated IPX driver with IPXODI.COM when using versions of VIPX
  101. later than 1.10.
  102.  
  103.  
  104. II.C.  Packet Size Limitations
  105. ------------------------------
  106.  
  107. VIPX.386 will only virtualize packets which are 8000 (decimal) bytes
  108. or less.  Any DOS and Windows IPX or SPX applications which use
  109. networks with a physical packet size greater than 8000 bytes may not
  110. work with VIPX.386.  For example, IBM Token Ring can be configured to
  111. run with 16K packets.  A request by a DOS or Windows IPX application
  112. to send a 16K packet will be truncated to 8000 bytes.  On the other
  113. hand, any 16K packet that is received by the LAN driver will be
  114. dropped because VIPX cannot allocate a packet big enough to handle
  115. it.
  116.  
  117.  
  118. II.D.  Memory Manager Limitations
  119. ---------------------------------
  120.  
  121. When an request is passed up from IPX, VIPX will immediately test the
  122. request buffer to see if it is in global memory already.  If it is in
  123. global memory, the request will be passed back down to IPX without
  124. any virtualization.  For example, a TSR loaded before Windows is
  125. considered to be in global memory.  If that TSR calls IPX, VIPX will
  126. test the requests and pass them back down to IPX.  This is done
  127. because there is no need to virtualize requests which are already
  128. global.
  129.  
  130. The use of UMB memory complicates the test for global memory.  There
  131. are two basic senarios.  Senario One is when a TSR has been loaded
  132. high before Windows was loaded.  In this case, IPX requests will come
  133. from global UMB memory.  VIPX will simply pass these requests back
  134. down to IPX.  Senario Two is when a TSR is loaded high in a Windows
  135. DOSBOX.  In this case, IPX requests will come from local UMB memory. 
  136. VIPX will virtualize these requests.
  137.  
  138. Using HIMEM.SYS and EMM386.SYS, all tests for global UMB memory will
  139. work properly under both of senarios.
  140.  
  141. However, there are some memory managers that do not work properly
  142. under Windows.  With these drivers, all LOCAL DOSBOX UMBs look as if
  143. they are GLOBAL UMBs.  Under Senario Two, when a TSR calls IPX, VIPX
  144. will test the request buffer and think that it is in global UMB
  145. memory when it is really in LOCAL UMB memory.  As a result, VIPX will
  146. pass a local ECB to IPX without virtualization.  The normal result of
  147. this is a hung machine or data corruption.
  148.  
  149. This problem has only been reported with Compaq's EXMEM.SYS (version
  150. unknown).
  151.  
  152. If you are using this memory manager, all TSRs USING THE IPX
  153. INTERFACE (INCLUDING IPX ITSELF) MUST BE LOADED IN CONVENTIONAL
  154. MEMORY.
  155.  
  156.  
  157. III.  System Settings in the SYSTEM.INI File
  158. --------------------------------------------
  159.  
  160. III.A.  [386Enh] Section
  161. ------------------------
  162.  
  163. III.A.1.  TimerCriticalSection
  164. ------------------------------
  165.  
  166. As of version 1.15 of VIPX, TimerCriticalSection is required to be set
  167. on.  The recommended setting is as follows:
  168.  
  169. [386Enh]
  170. TimerCriticalSection=10000
  171.  
  172. The reason for this parameter is to avoid a deadlock with the LAN IRQ
  173. Virtualization code.  See section III.B.5 VirtualizeIrq[0-F].
  174.  
  175.  
  176. III.B.  [VIPX] Section
  177. ------------------------
  178.  
  179. Under most circumstances, VIPX will work fine under the default
  180. configuration.  However, there may be some applications which require
  181. custom configuration of the driver.  This is a list of SYSTEM.INI
  182. parameters which can be used to configure VIPX:
  183.  
  184. [VIPX]
  185. VipxMappingPages                =[number of 4K pages]   (default = 16)
  186. VipxFailOverSizedPackets        =[ON|OFF|TRUE|FALSE]    (default = OFF)
  187. VpicdFix                        =[ON|OFF|TRUE|FALSE]    (default = ON)
  188. AutoIrqVirtualize               =[ON|OFF|TRUE|FALSE]    (default = ON)
  189. VirtualizeIrq[0-F]              =[ON|OFF|TRUE|FALSE]    (default = OFF)
  190.  
  191. (BELOW ARE CONFIGURATION PARAMETERS FOR BETA VERSIONS ONLY!)
  192. VipxErrorMessages               =[ON|OFF|TRUE|FALSE]    (default = OFF)
  193. VipxWarningMessages             =[ON|OFF|TRUE|FALSE]    (default = OFF)
  194. VipxBreakOnErrors               =[ON|OFF|TRUE|FALSE]    (default = OFF)
  195. VipxBreakOnWarnings             =[ON|OFF|TRUE|FALSE]    (default = OFF)
  196. VipxOutDebugStrOnErrors         =[ON|OFF|TRUE|FALSE]    (default = OFF)
  197. VipxOutDebugStrOnWarnings       =[ON|OFF|TRUE|FALSE]    (default = OFF)
  198.  
  199.  
  200. III.B.1.  VipxMappingPages
  201. --------------------------
  202.  
  203. This is the number of pages that VIPX can use to globalize requests
  204. to the global IPXODI.COM driver.  VIPX is not absolutely guaranteed
  205. to have all of these pages available at any one point, since this is
  206. the requested number of pages for SHARED global mapping which VIPX
  207. makes to the Windows VMM at initialization time.
  208.  
  209.  
  210. III.B.2.  VipxFailOverSizedPackets
  211. ----------------------------------
  212.  
  213. This parameter tells VIPX to fail any requests which require more
  214. than the maximum allowed globalization size.  The actual maximum will
  215. vary according to which media the user is on.  The absolute maximum
  216. is 8000 (decimal) bytes.  With media that have smaller packets than
  217. 8000 bytes, the maximum allowed size is the maximum packet size that
  218. can be put onto the media.
  219.  
  220.  
  221. III.B.3.  VpicdFix
  222. ------------------
  223. III.B.4.  AutoIrqVirtualize
  224. ---------------------------
  225.  
  226. VpicdFix and AutoIrqVirtualize are synonyms for the same option.
  227. The VPICD VxD has a problem of allowing an interrupt to be simulated
  228. to a VM which previously held the Windows Critical Section but currently
  229. does not.  (For more details, see III.B.5. VirtualizeIrq[0-F]).  This 
  230. causes a deadlock situation when LAN I/O is attemped in another VM.  
  231. To get around the problem, VIPX will take over the job of virtualizing 
  232. LAN IRQs and will simulate LAN interrupts only to the owner of the 
  233. Windows Critical Section.
  234.  
  235. This option is used to turn on or off the automatic detection and
  236. virtualization of LAN IRQs when ODI LAN drivers or dedicated LAN
  237. drivers are loaded.  The reason for this option is to allow OEMs to 
  238. virtualize their LAN drivers with their own VxDs.
  239.  
  240.  
  241.  
  242. III.B.5.  VirtualizeIrq[0-F]
  243. ----------------------------
  244.  
  245. This is option is automatically configured excepted on machines using
  246. the IBM LAN Support Program.  This parameter tells VIPX to virtualize
  247. the specified (hex) Interrupt ReQuest (IRQ) line.  This feature is
  248. mainly designed to assist the IBM LAN Support Program in avoiding the
  249. "Black Screen of Death".
  250.  
  251. Because of a design flaw in the VPICD.386 driver, network interrupts
  252. will sometimes deadlock a PC running Windows 3.X.  VIPX 1.15 attempts
  253. to avoid the deadlock by virtualizing the Network Interface Card's
  254. (NIC) IRQ(s).  With ODI and dedicated IPX (IPX.OBJ) drivers, VIPX
  255. will automatically read the configuration of the NIC from the driver
  256. and virtualize the selected IRQs.
  257.  
  258. The only time that VIPX cannot automatically detect the LAN IRQs is
  259. when using the IBM LAN Support Program with either SLANSUP.OBJ or 
  260. LANSUP.COM.  In this case, the LAN IRQ is not readable from the
  261. driver.  The reason (so I am told) is that IBM LAN Support does not
  262. have an API to read the IRQ setting on the NIC:  The only way to get
  263. this information is to read the NIC hardware itself.  The problem
  264. with doing this is that the hardware can be Token Ring, PCN2 or
  265. Ethernet.  This means that VIPX would have to be aware of all of the
  266. different configurations of all of these hardwares. Instead of this,
  267. VIPX requires the IBM LAN Support user to specify the NIC IRQ in the
  268. [VIPX] section of the SYSTEM.INI.  IRQs range from 0 to F (hex).  An
  269. example is listed below:
  270.  
  271. [VIPX]
  272. VirtualizeIrq2=TRUE
  273. VirtualizeIrq3=TRUE
  274.  
  275. In this example, VIPX will virtualize both IRQ 2 and IRQ 3.  VIPX can
  276. virtualize upto 4 different LAN IRQs.  The reason for virtualizing 
  277. multiple IRQs is to allow other LAN cards and protocols to be
  278. installed on the same PC and prevent them from deadlocking the
  279. machine.  For example, you may have IPX running through an NE2000
  280. card on IRQ 3 and TCP/IP running through to an IBM Token Ring card on
  281. IRQ 2.
  282.  
  283. One side note.  On an AT class PC, IRQ 2 is is really IRQ 9 on the
  284. physical machine.  IRQ 2 is a special cascade interrupt used to
  285. access IRQs 9-F.  Whenever a NIC raises the IRQ 2 line, the hardware
  286. configuration on the PC's bus actually issues an IRQ 9.  Also, if a
  287. NIC is configured to IRQ 2, the LAN driver during its initialization
  288. procedure will detect that the PC is an AT class machine and will
  289. correctly configure its software to use IRQ 9.
  290.  
  291. Getting to the point, the Windows VPICD VxD will not allow IRQ 2 to
  292. be exclusively virtualized by any VxD.  However, when VIPX detects
  293. that IRQ 2 is to be virtualized, it changes the IRQ to 9.  So if you
  294. are using the IBM LAN Support Program on an IBM Token Ring NIC set to
  295. IRQ 2, you should have not trouble.  The following two configurations
  296. are equivalent:
  297.  
  298. [VIPX]
  299. VirtualizeIrq2=TRUE
  300.  
  301. or
  302.  
  303. [VIPX]
  304. VirtualizeIrq9=TRUE
  305.  
  306. One important note when using VIPX 1.15 for higher:  Set the
  307. TimerCriticalSection under the [386Enh] section in the SYSTEM.INI as
  308. follows:
  309.  
  310. [386Enh]
  311. TimerCriticalSection=10000
  312.  
  313. The reason for this is that the timer interrupt can cause a deadlock
  314. with the virtualization of the the LAN interrupt.
  315.  
  316. You should note that the VirtualizeIrq[0-F] can be used to override
  317. any auto-virtualization of LAN IRQs.  For example, if your ODI LAN 
  318. driver is configured for IRQ 5, VIPX will automatically detect that
  319. IRQ 5 is to be virtualized.  However, if you have
  320.  
  321. [VIPX]
  322. VirtualizeIrq5=FALSE
  323.  
  324. in your SYSTEM.INI, VIPX will override the automatic detection of IRQ
  325. 5 and not virtualize the interrupt.
  326.  
  327. Another way to disable the auto-virtualization feature of VIPX is
  328. with the VpicdFix or AutoIrqVirtualize parameters (See III.B.3. 
  329. VpicdFix or III.B.4 AutoIrqVirtualize).
  330.  
  331.  
  332. III.B.6.  VipxErrorMessages
  333. ---------------------------
  334. III.B.7.  VipxWarningMessages
  335. -----------------------------
  336. III.B.8.  VipxOutDebugStrOnErrors
  337. ---------------------------------
  338. III.B.9.  VipxOutDebugStrOnWarnings
  339. -----------------------------------
  340. III.B.10. VipxBreakOnErrors
  341. ---------------------------
  342. III.B.11. VipxBreakOnWarnings
  343. -----------------------------
  344.  
  345. These parameters are only enabled in Beta versions of VIPX. With Beta
  346. software, the user can enable/disable these parameters to make VIPX
  347. print error/warning messages to the user screen and debugger screen.
  348. They can also instruct VIPX to execute a break point whenever an
  349. error/warning occurs so that debugging can be done on site.
  350.  
  351.  
  352. IV.  Programming to the VIPX Interface
  353. --------------------------------------
  354.  
  355. IV.A.  Getting the VIPX API Entry Point
  356. ---------------------------------------
  357.  
  358. VIPX supports API calls from 16-bit DOS and Windows applications. 
  359. This is the interface which NWIPXSPX.DLL uses to issue calls to VIPX.
  360.  
  361. To get the VIPX Device V86/PM API Entry Point, a 16-Bit DOS/Windows 
  362. application must do the following:
  363.  
  364.     DWORD VIPX_API_ADDR;
  365.     #define WIN_GET_DEV_API_ENTRY_PT 0x1684
  366.     #define VIPX_DEVICE_ID 0x0200
  367.     GetVipxApiAddr ()
  368.     {
  369.         _asm
  370.         {
  371.             mov     ax, WIN_GET_DEV_API_ENTRY_PT
  372.             mov     bx, VIPX_DEVICE_ID
  373.             int     2fh
  374.             mov     word ptr VIPX_API_ADDR+2, ES
  375.             mov     word ptr VIPX_API_ADDR, DI
  376.         }
  377.         if (!VIPX_API_ADDR)
  378.         {
  379.             print error - NO PM API! RIGHT VERS OF VIPX?
  380.             handle error
  381.         }
  382.     }
  383.  
  384. Once the API address is obtained, an application then can make a far
  385. call to the VIPX API Handler to do two primary functions:
  386.  
  387.     1.  Request an 16-Bit PM IPX/SPX operation (SendPacket, etc...).
  388.     2.  Request a device specific operation (GetVersion, GetDiagnotics).
  389.  
  390. The way that VIPX differentiates between the two types of APIs is by
  391. the value in the BH register.  If BH equals 00h, then VIPX will
  392. handle a 16-Bit Protected Mode IPX/SPX operation.  NOTE:  The V86
  393. IPX/SPX API interface (i.e. DOS application) is done through the
  394. globally loaded IPX TSR itself.  This way, DOS applications do not
  395. need to change the way they access the IPX/SPX APIs under Windows and
  396. DOS.
  397.  
  398. If BH equals the character 'V' (56h), then VIPX will will perform a
  399. V86/PM Device Specific API.  These APIs are listed below (and will be
  400. presented in full specification later):
  401.  
  402.     GetVipxVersion
  403.     GetVipxDiagnostics
  404.  
  405. For IPX/SPX operations (BH = 00h), a 16-bit Windows program must
  406. follow the IPX/SPX API specification in the NetWare System Calls-DOS
  407. manual under the Communications chapter.  Coverage of the DOS IPX/SPX
  408. APIs is beyond the scope of this document.  However, an example is
  409. provided below for reference:
  410.  
  411.     DWORD VIPX_API_ADDR;
  412.     int ccode;
  413.     CallPmIpxSpxApi ()
  414.     {
  415.         _asm
  416.         {
  417.             mov     bx, (IPX operation number)
  418.             (set other registers as specified by the function)
  419.             call    VIPX_API_ADDR
  420.             (save return registers as specified by the function)
  421.             ErrorCondition = (function error condition)
  422.         }
  423.         if (ErrorCondition)
  424.         {
  425.             print error - function failed!
  426.             handle error
  427.         }
  428.     }
  429.  
  430.  
  431. IV.B.  Memory Management Issues
  432. -------------------------------
  433.  
  434. One problem with using the IPX/SPX interface in Windows applications
  435. is that all memory used by VIPX must be page-locked.  Since
  436. VIPX is an asynchronous driver, all application ECBs, fragments and
  437. ESRs must be in locked memory to insure that they can be accessed
  438. during interrupt time.  If an ECB, fragment or ESR is in moveable
  439. memory, it could move when VIPX "owns" that memory.  For example,
  440. assume an ECB is located at 16-bit PM address 0A10:0000 in moveable
  441. memory.  Assume this address tranlates to 32-bit linear address
  442. 8140ACB0.  Assume also that the application gives the ECB to VIPX and
  443. VIPX commences to work on it.  Now if Windows does garbage
  444. collection, the linear mapping for the 16-bit address 0A10:0000 may
  445. change from 8140ABC0 to 81408100.  When VIPX tries to access the old
  446. linear address 8140ABC0, memory corruption will occur because VIPX is
  447. accessing the wrong memory.
  448.  
  449. To avoid memory corruption, follow the guidelines below (your
  450. comments on the effectiveness of these guidelines are appreciated):
  451.  
  452. 1.  Read Chapter 15 (Memory Management) and 16 (More Memory
  453. Management) of the MS Windows SDK Version 3.1.  These will explain
  454. many terms and concepts which will be discussed below.
  455.  
  456. 2.  Define all ESRs, routines called by ESRs and data accessed by
  457. ESRs in one module.  In the .DEF file for your project, define the
  458. module's code and data segments as FIXED.
  459.  
  460. 3.  Use GlobalPageLock() on all dynamically allocated heap memory
  461. (via GlobalAlloc() or LocalAlloc()) which is to be used by VIPX or an
  462. ESR.  GlobalPageLock() informs the GUI kernel memory manager to fix
  463. the linear base of the selector until a GlobalPageUnlock() is done on
  464. the memory.
  465.  
  466. 4.  When using GlobalAlloc(), use the GMEM_DDESHARE and GMEM_FIXED
  467. options.  GMEM_DDESHARE will allocate the memory (so I am told) high
  468. up on the global heap.  This will avoid using conventional memory and
  469. will allow room for other Windows applications to load after your
  470. application has been loaded and initialized.  The GMEM_FIXED option
  471. tells the GUI kernel memory manager that the memory is FIXED.  This
  472. does not mean that the linear address will remain unchanged, but it
  473. does mean that the segmented address (selector:offset) will not
  474. change.  After the call to GlobalAlloc(), call GlobalLock() to
  475. derefernce the global memory handle.  This provides a far pointer
  476. (selector:offset) to the memory which will not change.  Next call
  477. GlobalPageLock() to fix the linear base of the selector so that the
  478. 32-bit linear mapping of the selector:offset does not change.
  479.  
  480. 5.  If local moveable or discardable code or data segments are used
  481. for ECBs, fragments or ESRs, use GlobalPageLock() on those segments.
  482.  
  483. 6.  After you load your application and then you get a message from
  484. Windows that there are not enough resources to load another program,
  485. try changing the attributes of your segments. I believe this
  486. condition occurs because too much conventional memory has been
  487. allocated to fixed memory objects and Windows needs to allocate
  488. conventional memory to load a program.  One strategy may be to do a
  489. GlobalCompact() with a argument of -1 at initialization time.  This
  490. will move all moveable segments and discard all discardable
  491. segments.  Next call GlobalAlloc() for one large moveable global
  492. object which contains all of your ECBs, fragments and data which must
  493. be accessed from VIPX or your ESRs.  Next call GlobalLock() to
  494. dereference the global handle.  Last call GlobalPageLock() to fix the
  495. linear address of the object.  (I have not tried this yet, so there
  496. may be some problems with it).  Also make your ESR code as small as
  497. possible using PostMessage to indicate to your application that an
  498. ECB has completed.  This way, your FIXED code segment will have
  499. little impact on conventional memory.
  500.  
  501. 7.  Read Chapter 4 of The Windows Programmer's Guide to DLLs and
  502. Memory Management by Mike Klein.
  503.  
  504.  
  505. IV.C.  APIs Specific to VIPX.386
  506. --------------------------------
  507.  
  508. There are two APIs via the VIPX API entry point which are specific
  509. to VIPX.386 only.  These are:
  510.  
  511.     GetVipxVersion
  512.     GetVipxDiagnostics
  513.  
  514. The device specific APIs are called in the same manner as the IPX/SPX
  515. APIs except that BH = 56h.  Some sample code is listed below:
  516.  
  517.     DWORD VIPX_API_ADDR;
  518.     #define VIPX_DEVICE_API_TAG 'V'
  519.     int ccode;
  520.     CallVipxDeviceSpecificApi ()
  521.     {
  522.         _asm
  523.         {
  524.             mov     bh, VIPX_DEVICE_API_TAG
  525.             mov     bl, api_function_number
  526.             (set other registers for API function)
  527.             call    VIPX_API_ADDR
  528.             mov     ccode, ax
  529.         }
  530.         if (!ccode)
  531.         {
  532.             print error - api function failed!
  533.             handle error
  534.         }
  535.     }
  536.  
  537. The full API specification for GetVipxVersion is as follows:
  538.  
  539. GetVIPXVersion
  540.  
  541.     This procedure puts the VIPX major and minor version numbers into
  542.     the client AH and AL.
  543.  
  544.     To call this API a DOSBOX or Windows 3.0 application
  545.     must first get the VIPX Device API entry point (see
  546.     VIPX_V86_API_Handler or VIPX_PM_Handler).  Then
  547.     it must do the following:
  548.  
  549. Example:
  550.  
  551.     DWORD VIPX_API_ADDR;
  552.     BYTE VIPX_Major_Version;
  553.     BYTE VIPX_Minor_Version;
  554.     #define VIPX_DEVICE_API_TAG 'V'
  555.     #define VIPX_GET_VERSION 0x00
  556.     int ccode;
  557.     main ()
  558.     {
  559.         _asm
  560.         {
  561.             mov     bh, VIPX_DEVICE_API_TAG
  562.             mov     bl, VIPX_GET_VERSION
  563.             call    VIPX_API_ADDR
  564.             mov     ccode, ax
  565.             mov     VIPX_Major_Version, bh
  566.             mov     VIPX_Minor_Version, bl
  567.         }
  568.         if (!ccode)
  569.         {
  570.             print error - api function failed!
  571.             handle error
  572.         }
  573.     }
  574.  
  575. Entry:
  576.     BH      056h - VIPX Device API
  577.     BL      0 - Get VIPX Version function
  578.  
  579. Exit:
  580.     BH      VIPX Major Version
  581.     BL      VIPX Minor Version
  582.     AX      Return code
  583.             0000h - success
  584.  
  585. Uses:
  586.     AX, BX, Flags
  587.  
  588. The full API specification for GetVIPXDiagnostics is as follows:
  589.  
  590. GetVIPXDiagnostics
  591.  
  592.     This procedure puts the diagnostics for VIPX into the
  593.     specified client diagnostic buffer.  The buffer is 112 bytes 
  594.     and has the following structure:
  595.  
  596.         VIPXDiagStruc   STRUC
  597.             MallocCount                     dd      ?
  598.             MallocErrorCount                dd      ?
  599.             FreeCount                       dd      ?
  600.             AllocPageCount                  dd      ?
  601.             FreePageCount                   dd      ?
  602.             PMRequestCount                  dd      ?
  603.             TSRServiceCount                 dd      ?
  604.             ServiceAESEventCount            dd      ?
  605.             ServiceIPXEventCount            dd      ?
  606.             PostEventCount                  dd      ?
  607.             IPXGetECBCount                  dd      ?
  608.             IPXGetECBBadPacketCount         dd      ?
  609.             IPXGetECBOutOfResource          dd      ?
  610.             IPXCountECBCount                dd      ?
  611.             IPXReturnECBCount               dd      ?
  612.             IPXRequestCount                 dd      ?
  613.             FreeECBCount                    dd      ?
  614.             AllocECBCount                   dd      ?
  615.             LockECBCount                    dd      ?
  616.             UnlockECBCount                  dd      ?
  617.             OpenSocketCount                 dd      ?
  618.             CloseSocketCount                dd      ?
  619.             SendPacketCount                 dd      ?
  620.             ListenCount                     dd      ?
  621.             IPXScheduleEventCount           dd      ?
  622.             AESScheduleEventCount           dd      ?
  623.             CancelECBCount                  dd      ?
  624.             PMInt2FCount                    dd      ?
  625.             AllocPageErrorCount             dd      ?
  626.             FreePageErrorCount              dd      ?
  627.             FreeECBErrorCount               dd      ?
  628.             MaxVipxAllocSize                dd      ?
  629.             MaxVipxFragsSize                dd      ?
  630.             TsrServiceReenterCnt            dd      ?
  631.             IPXGetEcbSktNotOpenCnt          dd      ?
  632.             IPXGetEcbNoEcbAvailCnt          dd      ?
  633.         ENDS
  634.  
  635.     NOTE:  Starting with VIPX 1.16, MaxVipxAllocSize, MaxVipxFragsSize, and
  636.     TsrServiceReenterCnt have been updated to DWORDs.  Also IPXGetEcbSktNotOpenCnt
  637.     and IPXGetEcbNoEcbAvailCnt have been added.
  638.  
  639.  
  640.     To call this API a DOSBOX or Windows 3.0 application
  641.     must first get the VIPX Device API entry point (see
  642.     VIPX_V86_API_Handler or VIPX_PM_Handler).  Then
  643.     it must do the following:
  644.  
  645. Example:
  646.  
  647.     DWORD VIPX_API_ADDR;
  648.     VIPXDiagStruc Diag;
  649.     #define VIPX_DEVICE_API_TAG 'V'
  650.     #define VIPX_GET_DIAGNOSTIC 0x01
  651.     int ccode;
  652.     main ()
  653.     {
  654.         _asm
  655.         {
  656.             mov     bh, VIPX_DEVICE_API_TAG
  657.             mov     bl, VIPX_GET_DIAGNOSTIC
  658.             mov     ax,ds
  659.             mov     es,ax
  660.             mov     di, offset Diag
  661.             mov     cx, size VIPXDiagStruc
  662.             call    VIPX_API_ADDR
  663.             mov     ccode, ax
  664.         }
  665.         if (!ccode)
  666.         {
  667.             print error - api function failed!
  668.             handle error
  669.         }
  670.     }
  671.  
  672. Entry:
  673.     BH      056h - VIPX Device API
  674.     BL      1 - Get VIPX Diagnostics
  675.     ES:SI   ptr to client VIPX Diagnostics buffer.
  676.     CX      Length of diagnostics buffer in bytes
  677.  
  678. Exit:
  679.     ES:SI   client's buffer now has VIPX diags.
  680.     AX      Return code
  681.             0000h - success
  682.             0FFFEh - diagnostics buffer too short
  683.  
  684. Uses:
  685.     AX, SI, ES, Flags
  686.  
  687.  
  688. IV.D.  Handling Reentrancy Issues
  689. ---------------------------------
  690.  
  691. A Windows application which uses the IPX/SPX APIs via VIPX must
  692. handle reentrancy issues.  Since the IPX/SPX API is interrupt driven,
  693. any code running while interrupts are enabled can (and will) be
  694. interrupted at any time by IPX/SPX.  When an IPX event completes, an
  695. ESR for that event is called.  The ESR is entered with interrupts
  696. disabled.  If that ESR reenables interrupts, then it can be
  697. reentered.  This is usually a problem if:
  698.  
  699. 1.  The application has limited stack space
  700. 2.  The application's ESR uses global variables
  701.  
  702. An ESR must protect itself from these two reentrancy issues if it
  703. reenables interrupts.  Interrupts can be reenabled by several
  704. different places.  One of the most notorious is the PostMessage()
  705. Windows call.  Any ESR using PostMessage() must be reentrant. 
  706. Another notorious enabler of interrupts is IPXSendPacket. 
  707. IPXSendPacket will call the LSL to transmit a packet.  The LSL,
  708. however,  will reenable interrupts during transmission of a packet. 
  709. This can cause reentrancy into any ESR that calls IPXSendPacket.
  710.  
  711. An ESR can handle reentrancy issues by
  712.  
  713. 1.  Switching to a stack exclusively for the ESR to use.
  714. 2.  Provide a reetrancy queue.
  715.  
  716. The stack switching is usually done in assembly.  It is by far the
  717. easiest and most efficient way to protect your stack.  The code for
  718. stack switching is listed below:
  719.  
  720. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  721. memL equ 1 ; large model
  722. ?PLM = 1 ; pascal conventions
  723. ?WIN = 1 ; epilogue/prolog code
  724.  
  725.  
  726. INCLUDE cmacros.inc
  727.  
  728. sBegin  DATA
  729.     assumes DS, DATA
  730.  
  731. ReenterCnt      db      0
  732.  
  733. OldSs           dw      0
  734. OldSp           dw      0
  735.  
  736. EsrStack        db      4096 dup (0)
  737.  
  738. EndEsrStack label byte
  739.  
  740. sEnd    DATA
  741.  
  742. sBegin  CODE
  743.     assumes CS, CODE
  744. ;       
  745. ;       This is the front-end procedure to the C function "ReceiveESR"
  746. ;
  747.  
  748. externFP cESR
  749. PUBLIC aESR
  750. aESR    proc FAR
  751.  
  752.     nop
  753.     nop
  754.     nop
  755.     nop
  756.  
  757.     ; Don't know why Windows needs to inc bp
  758.     inc     bp
  759.     push    bp
  760.     mov     bp, sp
  761.  
  762.     ; Save data segment
  763.     push    ds
  764.  
  765.     ; Fix to our data segmented
  766.     mov     ax, DGROUP
  767.     mov     ds, ax
  768.  
  769.     ; Update our reenter cnt
  770.     inc     ReenterCnt
  771.  
  772.     ; Do we need to switch stack to ESR stack?
  773.     cmp     ReenterCnt, 2           ;are we reentering?
  774.     jae     CallEsr                 ;y: call esr
  775.     mov     ax, ss
  776.     cmp     ax, DGROUP              ;same stack seg?
  777.     jne     SwitchStack             ;n: switch
  778.     lea     ax, EsrStack            ;ax=offset of EsrStack
  779.     cmp     sp, ax                  ;below the esr stack?
  780.     jb      SwitchStack             ;y: switch
  781.     lea     ax, EndEsrStack         ;ax=offset of end of EsrStack
  782.     cmp     sp, ax                  ;above the esr stack
  783.     jae     SwitchStack             ;y: switch
  784.  
  785.     ; Call the esr
  786. CallEsr:
  787.     push    es
  788.     push    si
  789.     cCall   cESR
  790.     lea     sp, [bp-2]
  791.  
  792.     ; Update reenter cnt
  793.     dec     ReenterCnt
  794.  
  795.     ; Switch stacks back?
  796.     jz      SwitchStackBack
  797.  
  798.     ; return to IPX
  799. EsrExit:
  800.  
  801.     ; restore ds and bp
  802.     pop     ds
  803.     pop     bp
  804.     dec     bp
  805.  
  806.     ret
  807.  
  808. SwitchStack:
  809.     mov     ax, sp
  810.     mov     OldSp, ax
  811.     mov     ax, ss
  812.     mov     OldSs, ax
  813.     lea     ax, EndEsrStack
  814.     mov     sp, ax
  815.     mov     ax, ds
  816.     mov     ss, ax
  817.     jmp     CallEsr
  818.  
  819. SwitchStackBack:
  820.     mov     ax, OldSp
  821.     mov     sp, ax
  822.     mov     ax, OldSs
  823.     mov     ss, ax
  824.     jmp     EsrExit
  825.  
  826. aESR    endp
  827.  
  828. sEnd    CODE
  829.     end
  830.  
  831. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  832.  
  833. The above code switched to a 4K stack before calling the main ESR
  834. code.  Notice that there is a reentrancy count to determine if a stack
  835. switch should occur.  This is used to prevent switching the stack
  836. again if the ESR is reentered.
  837.  
  838. The main ESR code is written in C.  An example is listed below:
  839.  
  840. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  841. void far pascal cESR(ECB far *esr_ecb)
  842. {
  843.     static ECB FAR * EcbQueue;
  844.     static ECB FAR * EcbQueueTail = (ECB FAR *)&EcbQueue;
  845.     static WORD ReenterFlag = 0;
  846.  
  847.     // Check for reentrancy
  848.     if (ReenterFlag)
  849.     {
  850.         // Enqueue the ECB
  851.         EcbQueueTail->Link = (void far *)esr_ecb;
  852.         EcbQueueTail = (ECB far *)esr_ecb;
  853.         esr_ecb->Link = NULL;
  854.         return;
  855.     }
  856.  
  857.     // Set the reentrany flag to indicate that we can't allow 
  858.     // another thread through the ESR.
  859.     ReenterFlag++;
  860.  
  861.     // Process the current ECB
  862. NextEcb:
  863.  
  864.     // Do some work here if need be -- BUT BE QUICK!
  865.     ...
  866.  
  867.     // Post a message to the Windows App that the event happened
  868.     PostMessage (
  869.         hMainWnd, 
  870.         USER_DEFINED_IPX_EVENT_HAPPPENED, 
  871.         0, 
  872.         (DWORD) esr_ecb);
  873.  
  874.     // Check for anything on the queue
  875. CheckQueue:
  876.     if (EcbQueue)
  877.     {
  878.         esr_ecb = EcbQueue;
  879.         EcbQueue = (ECB FAR *)(EcbQueue->Link);
  880.         if (!EcbQueue)
  881.             EcbQueueTail = (ECB FAR *)&EcbQueue;
  882.         goto NextEcb;
  883.     }
  884.  
  885.     // Reset the reentrancy flag to indicate that the 
  886.     // first thread is done
  887.     ReenterFlag--;
  888. }
  889.  
  890. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  891.  
  892. One side note about the code above.  PostMessage() will queue a
  893. message onto an applications message queue.  This queue is limited in
  894. size.  If the application is fairly LAN I/O intensive, the message
  895. queue can be overrun.  In this case, PostMessage() will simply drop 
  896. the message.  If this happens, your application will never get
  897. notified of the event.  To insure that messages are never dropped,
  898. you may want to increase the size of your message queue.  Another
  899. alternative is to have the message indicate that an event happened
  900. and the message loop will then poll all ECBs to determine which ones
  901. completed.  This way, if a message is dropped, it won't matter:  the
  902. main message loop will still detect that the ECB completed and will
  903. be able to reuse the ECB.
  904.  
  905.  
  906.  
  907. IV.E.  Using NWIPXSPX.DLL
  908. -------------------------
  909.  
  910. Most Windows applications will use the IPX/SPX APIs via the
  911. NWIPXSPX.DLL.  The reason is that NWIPXSPX.DLL address the issues for
  912. both Standard and Enhanced mode Windows.  Under Standard mode,
  913. NWIPXSPX will use TBMI2.COM to virtualize the local ECBs passed to
  914. it.  Under Enhanced mode, NWIPXSPX.DLL will use VIPX.386 to
  915. virtualize ECBs.  If you Windows application is targeted toward
  916. Enhanced mode only, then you may find it more convenient to program
  917. directly to the VIPX interface in assembly.  However, if you are
  918. interested in using an established C interface for IPX/SPX, then
  919. NWIPXSPX.DLL is the way to go.  There is example code using NWIPXSPX.DLL 
  920. on the Compuserve NOVDEV forum.
  921.  
  922.  
  923. V.  Global DOS TSRs using IPX/SPX
  924. ---------------------------------
  925.  
  926. TSRs loaded before Windows can call IPX without the intervention of
  927. VIPX.  This is done by ORing the BX register with 8000h (i.e. setting
  928. the high-bit of the function number).  This is a previously
  929. undocumented feature in IPX to support the globally loaded NETX
  930. driver.  When IPX sees that a request has the high-bit set, it will
  931. assume that the request came from a globally loaded TSR and will not
  932. forward the request to VIPX.
  933.  
  934. The only problem with the high-bit is that SPX does not recognize
  935. it.  When SPX gets a request with a high-bit, SPX ignores it and
  936. reposts the ECB to IPX without the preserving the high-bit.  VIPX
  937. will then get the IPX request and then determine if the ECB is really
  938. loaded in global memory.  If so, then it will simply pass the global
  939. request back down to IPX with the high-bit set.
  940.  
  941.  
  942.